package ai4games_tests.tests;

import java.awt.Point;

import game2dai.Domain;
import game2dai.World;
import game2dai.entities.Building;
import game2dai.entities.Obstacle;
import game2dai.entities.Vehicle;
import game2dai.entityshapes.ps.BuildingPic;
import game2dai.entityshapes.ps.Hints;
import game2dai.entityshapes.ps.PersonPic;
import game2dai.entityshapes.ps.Umbrella;
import game2dai.graph.Graph;
import game2dai.graph.GraphEdge;
import game2dai.graph.GraphNode;
import game2dai.maths.Vector2D;
import game2dai.steering.AutoPilot;
import game2dai.steering.SBF;
import game2dai.utils.StopWatch;
import processing.core.PApplet;

public class Demo_002 extends PApplet {
	Domain wd;
	World world;
	Building[] buildings;
	Obstacle[] stalls;
	Vehicle[] tourists;
	Vehicle[] patrolLeader;

	Graph routes;
	GraphNode[] nodes;
	GraphNode dest = null;
	GraphEdge[] edges;
	
	int[] stallCol1, stallCol2;

	StopWatch watch;

	Vector2D worldMouse = new Vector2D();

	int partSize = 200, partOverlap = 80;
	int nbrTourists = 1000;
	int nbrPatrols = 9;
	int followPatrol = -1;
	int displayBorderX = 80, displayBorderY = 80;
	
	float HALF_CITY_SIZE = 4000;

	public void setup(){
		size(480,480);
		cursor(CROSS);
		stallCol1 = new int[] { color(174,44,79), color(255,227,40) };
		stallCol2 = new int[] { color(1,8,99), color(251,190,37), color(228,4,4), color(14,143,86) };

		world = new World(width, height, partSize, partOverlap);
//		world = new World(width, height);
		world.panPixelX(width/2);
		world.panPixelY(height/2);

		println(world.worldUpdateTime + " s");

//		world.setSCale(1.2f);
		world.scale(0.3f);

		world.noOverlap(true);
//		System.out.println(world.getViewOnWorld());
//		world.panWorldX(40);
//		System.out.println(world.getViewOnWorld());

		wd = new Domain(-HALF_CITY_SIZE-80, -HALF_CITY_SIZE-80, HALF_CITY_SIZE+80, HALF_CITY_SIZE+80);
		// Get the navigation map
		routes = Graph.makeFromXML(this, "ai4g/d002_graph.xml");
		nodes = routes.getNodeArray();
		edges = routes.getEdgeArray();
		
		// Create the buildings
		buildings = Building.makeFromXML(this, "ai4g/d002_building.xml");
		int bcol = 0;
		for(Building b : buildings){
			bcol = color(random(160,200), random(160,200), random(160,200));
			BuildingPic bpic = new BuildingPic(this, bcol, color(0), 4.0f);
			b.renderer(bpic);
			world.add(b);
		}


		// Create all the moving objects - start with the patrol
		AutoPilot sb = new AutoPilot();	// steering behaviour used as base for others
//		sb.setWallAvoidDetails(3, 2.8, 20);
		sb.wallAvoidFactors(3, 20, 2.8, true);
		sb.wallAvoidOn();
//		sb.enableBehaviours(SBF.WALL_AVOID);
		// Create the patrols
		patrolLeader = new Vehicle[nbrPatrols];
		for(int i = 0; i < patrolLeader.length; i++){
			makePatrol(i);
		}

		// Now create the tourists
		sb.wanderFactors(100, 10, 100);
		
//		sb.setWeight(SBF.SEPARATION, 30);
		sb.separationWeight(30);
		AutoPilot touristSB = (AutoPilot) sb.clone();
//		touristSB.enableBehaviours(SBF.WANDER | SBF.WALL_AVOID);
		touristSB.wanderOn().wallAvoidOn();
//		touristSB.setWanderDetails(12, 20, 80);
		
		touristSB.wanderFactors(80, 10, 12);

//		touristSB.setWallAvoidDetails(5, 2.8, 20);
		touristSB.wallAvoidFactors(5, 20, 2.8, true);
		
		//		touristSB.setWeight(SBF.WALL_AVOID, 400);
		tourists = new Vehicle[nbrTourists];
		for(int i = 0; i < tourists.length; i++){
			tourists[i] = new Vehicle( new Vector2D(random(-HALF_CITY_SIZE, HALF_CITY_SIZE), random(-HALF_CITY_SIZE, HALF_CITY_SIZE)), 8,
					new Vector2D(40 * (random(1.0f) - 0.5f), 40 * (random(1.0f) - 0.5)), 40 + 28 * random(1.0f),
					new Vector2D(random(1.0f) - 0.5f, random(1.0f) - 0.5f), 1, 1, 700);
			PersonPic apic = new PersonPic(this, 10, color(255,160,255), color(145,64,47), color(0), 1);
			tourists[i].AP((AutoPilot) touristSB.clone());
			tourists[i].renderer(apic);
			tourists[i].worldDomain(wd);
			world.add(tourists[i]);
		}

		println("Number of buildings = " + world.nbr_buildings);
		println("Number of obstacles = " + world.nbr_obstacles);
		println("Number of walls     = " + world.nbr_walls);
		println("Number of movers    = " + world.nbr_movers);
		
		println("Nbr nodes = "+nodes.length);
		println("Nbr edges = "+edges.length);

		watch = new StopWatch(); // last thing to be done in setup
	}

	public void makePatrol(int patrolNumber){
		// Get start and end nodes
		float x0,x1,y0,y1,dx,dy;
		GraphNode start, dest;
		Vehicle[] troop;
		troop = Vehicle.makeFromXML(this, "ai4g/d002_patrol.xml");
		do {
			x0 = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
			y0 = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
			x1 = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
			y1 = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
		} while(abs(x1-x0) < 500 && abs(y1-y0) < 500);
		start = routes.getNodeNearest(x0, y0, 0);
		dest = routes.getNodeNearest(x1, y1, 0);

		AutoPilot sb = new AutoPilot();	// steering behaviour used as base for others
//		sb.setWallAvoidDetails(5, 2.8, 20);
		sb.wallAvoidFactors(5, 20, 2.8, true);
		sb.wallAvoidOn(); // enableBehaviours(SBF.WALL_AVOID);	
		for(int i = 0;  i < troop.length; i++){
			troop[i].moveBy(start.xf(), start.yf());
			PersonPic apic = new PersonPic(this, 10, color(160,160,255), color(160,144,70), color(0), 1);
			AutoPilot patrolSB = (AutoPilot) sb.clone();
			if(i == 0){
				apic.bodyFill(color(100,100,255));
				patrolSB.pathOn(); // enableBehaviours(SBF.PATH);
				patrolSB.arriveWeight(900);
			}
			else {
				patrolSB.offsetPursuitOn(troop[0], Vector2D.sub(troop[i].pos(), troop[0].pos())); // enableBehaviours(SBF.OFFSET_PURSUIT);
				patrolSB.offsetPursuitWeight(30);
			}
			troop[i].AP(patrolSB);
			troop[i].renderer(apic);
			troop[i].worldDomain(wd);
			world.add(troop[i]);
		}
		troop[0].AP().pathAddToRoute(routes, dest);
		patrolLeader[patrolNumber] = troop[0];
	}

	public void draw(){
		float deltaTime = (float) watch.getElapsedTime();
		updatePatrolRoutes();
		if(followPatrol >= 0){
			Point p = world.world2pixel(patrolLeader[followPatrol].pos(), null);
			int dx = 0, dy = 0;
			if(p.x < displayBorderX)
				dx = displayBorderX - p.x;
			else if(p.x > width - displayBorderX)
				dx = width - displayBorderX - p.x;		
			if(p.y < displayBorderY)
				dy = displayBorderY - p.y;
			else if(p.y > height - displayBorderY)
				dy = height - displayBorderY - p.y;
			world.panPixelXY(dx, dy);
		}
		background(250);
		pushMatrix();
		translate((float)world.xOffset(), (float)(world.yOffset()));
		scale((float) world.scale());
		world.update(deltaTime);
		world.draw();
		//		showPartitions();
//		showEdges();
//		showNodes();
		popMatrix();
	}

	public void updatePatrolRoutes(){
		GraphNode dest;
		for(int i = 0; i < patrolLeader.length; i++){
			if(patrolLeader[i].AP().pathRoute().size() < 3){
				float x = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
				float y = random(-HALF_CITY_SIZE, HALF_CITY_SIZE);
				dest = routes.getNodeNearest(x, y, 0);
				patrolLeader[i].AP().pathAddToRoute(routes, dest);
			}
		}
	}

	public void keyReleased(){
		int index = "1234567890".indexOf(key);
		if(index == 9){
			followPatrol = -1;
		}
		else if(index >= 0 && index < patrolLeader.length){
			followPatrol = index;
			Vector2D pos = patrolLeader[followPatrol].pos();
			world.moveToWorldXY(pos.x, pos.y);
		}
	}
	
	public void mouseDragged(){
		if(followPatrol < 0)
			world.panPixelXY(mouseX - pmouseX, mouseY - pmouseY);
	}

	public void mouseClicked(){
		println(frameRate);
	}

	public void showNodes(){
		pushStyle();
		noStroke();
		int r;
		ellipseMode(CENTER);
		for(GraphNode node : nodes){
			if(dest == null || dest != node){
				r = 18;
				fill(0,255,0);
			}
			else {
				r = 24;
				fill(0,160,0);
			}
			ellipse(node.xf(), node.yf(), r, r);
		}
		popStyle();
	}

	public void showEdges(){
		pushStyle();
		noFill();
		strokeWeight(2);
		stroke(255,0,0);
		for(GraphEdge edge : edges){
			line(edge.from().xf(),edge.from().yf(), edge.to().xf(),edge.to().yf());

		}
		popStyle();
	
	}
	public void showPartitions(){
		noFill();
		stroke(255,0,0,128);
		strokeWeight(1.0f);
		int p = -4000;
		while(p <= 4000){
			line(-4000, p, 4000, p);
			line(p, -4000, p, 4000);
			p += partSize;
		}
	}

	public void setHints(boolean showHints){
		for(Vehicle tourist : tourists)
			((PersonPic)tourist.renderer()).showHints(Hints.HINT_HEADING | Hints.HINT_VELOCITY);
		for(Vehicle patrolman : patrolLeader)
			((PersonPic)patrolman.renderer()).showHints(Hints.HINT_HEADING | Hints.HINT_VELOCITY);
		for(Obstacle stall : stalls)
			((Umbrella)stall.renderer()).showHints(Hints.HINT_COLLISION);
	}

}
